#ifndef __DALELIB_MD2_H__
#define __DALELIB_MD2_H__

#include <assert.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;

#include <dalelib/v2.h>
#include <dalelib/v3.h>
#include <dalelib/m4.h>

namespace dalelib
{

#define MD2VERSION (8)

v3 NormalLUT[];

//********************** structures *******************************
typedef struct
{
	int ident;              // magic number. must be equal to "IDP2"
	int version;            // md2 version. must be equal to 8

	int skinwidth;          // width of the texture
	int skinheight;         // height of the texture
	int framesize;          // size of one frame in bytes

	int num_skins;          // number of textures
	int num_vertices;            // number of vertices
	int num_st;             // number of texture coordinates
	int num_triangles;           // number of triangles
	int num_glcmds;         // number of opengl commands
	int num_frames;         // total number of frames

	int offset_skins;          // offset to skin names (64 bytes each)
	int offset_st;             // offset to s-t texture coordinates
	int offset_tris;           // offset to triangles
	int offset_frames;         // offset to frame data
	int offset_glcmds;         // offset to opengl commands
	int offset_end;            // offset to end of file
} MD2Header;

typedef float md2_vec3[3]; // vector

struct md2_skin { char name[64]; }; // texture file name

struct md2_texCoord { short s, t; }; // texture coords

struct md2_triangle { unsigned short vertex[3], st[3]; };

struct md2_vertex { unsigned char v[3], normalIndex; };

struct md2_frame { md2_vec3 scale, translate; char name[16]; md2_vertex *verts; };


//********************** structures *******************************
class MD2
{
private:

	struct Vertex
	{
		v3 _pos, _nrm;
	};

	struct Triangle
	{
		int _verts[3];
		int _uv[3];
	};

	typedef v2 TexCoord;

	struct Vert
	{
		v3 _pos;
		int _nrmidx;
		Vert() : _nrmidx(0) { }
	};

	struct Frame
	{
		string _name;
		Vert *_data;
	};

	unsigned int _num_vertices;
	Vertex *_vertices;

	unsigned int _num_triangles;
	Triangle *_triangles;

	unsigned int _num_texcoords;
	TexCoord *_texcoords;

	typedef vector<Frame> FrameList;
	FrameList _frames;

	int _frame;


public:

	MD2() : _frame(0) { }

	~MD2()
	{
		for(unsigned int i=0; i<_frames.size(); ++i) delete [] _frames[i]._data;
		delete [] _vertices;
		delete [] _triangles;
		delete [] _texcoords;
	}

	int getNumFrames() { return _frames.size(); }

	void transform(const m4 _Mat)
	{
		for(unsigned int j=0; j<_frames.size(); ++j)
		{
			for(unsigned int i=0; i<_num_vertices; ++i)
			{
				_frames[j]._data[i]._pos = _Mat.transformPoint(_frames[j]._data[i]._pos);
			}
		}
	}

	int load(const string _FileName)
	{
		MD2Header header;

		ifstream fin( _FileName.c_str(), ios_base::in|ios_base::binary );
		if( !fin ) { cout << "can't open mesh file for loading" << endl; return -1; }

		fin.read(reinterpret_cast<char*>(&header),sizeof(MD2Header));

//for(int i=0; i<17; ++i) cout << "int " << i << ": " << reinterpret_cast<int*>(&header)[i] << endl;

		if( header.ident!=0x32504449 || header.version!=MD2VERSION ) // first 4 bytes should be "IDP2" and next int should be 8
		{
			cout << _FileName << " is not a valid md2 file" << endl;
			return -1;
		}

		_num_vertices = header.num_vertices;
		_vertices = new Vertex[_num_vertices];
/*
		{ // *** load skins ***
			md2_skin *skins = (md2_skin *)new md2_skin[header.num_skins];
			fin.seekg(header.offset_skins);
			fin.read((char*)skins, sizeof(md2_skin)*header.num_skins);
			delete [] skins;
		}
*/
		{ // *** load texcoords ***
			md2_texCoord *texcoords = (md2_texCoord *)new md2_texCoord[header.num_st];
			fin.seekg(header.offset_st);
			fin.read((char*)texcoords, sizeof(md2_texCoord)*header.num_st);
			_num_texcoords = header.num_st;
			_texcoords = new TexCoord[_num_texcoords];
			for(int i=0; i<header.num_st; ++i)
			{
				_texcoords[i] = v2(float(texcoords[i].s)/header.skinwidth,float(texcoords[i].t)/header.skinheight);
			}
			delete [] texcoords;
		}
		{ // *** load triangles ***
			md2_triangle *triangles = (md2_triangle *)new md2_triangle[header.num_triangles];
			fin.seekg(header.offset_tris);
			fin.read((char*)triangles, sizeof(md2_triangle)*header.num_triangles);
			_num_triangles = header.num_triangles;
			_triangles = new Triangle[_num_triangles];
			for(int i=0; i<header.num_triangles; ++i)
			{
				_triangles[i]._verts[0] = triangles[i].vertex[0];
				_triangles[i]._verts[1] = triangles[i].vertex[1];
				_triangles[i]._verts[2] = triangles[i].vertex[2];
				_triangles[i]._uv[0] = triangles[i].st[0];
				_triangles[i]._uv[1] = triangles[i].st[1];
				_triangles[i]._uv[2] = triangles[i].st[2];
			}
			delete [] triangles;
		}
		{ // *** load gl commands ***
			int *glcmds = (int *)new int[header.num_glcmds];
			fin.seekg(header.offset_glcmds);
			fin.read((char*)glcmds, sizeof(int)*header.num_glcmds);
			delete [] glcmds;
		}
		{ // *** load frames ***
			md2_frame *frames = (md2_frame *)new md2_frame[header.num_frames];
			fin.seekg(header.offset_frames);
			for(int i=0; i<header.num_frames; ++i)
			{
				frames[i].verts = (md2_vertex *)new md2_vertex[header.num_vertices]; // create temp frame array
				fin.read((char*)frames[i].scale, sizeof(md2_vec3));
				fin.read((char*)frames[i].translate, sizeof(md2_vec3));
				fin.read((char*)frames[i].name, sizeof(char)*16);
				fin.read((char*)frames[i].verts, sizeof(md2_vertex)*header.num_vertices);

				Frame f;
				f._data = new Vert[_num_vertices]; // create real frame array
				f._name = string(frames[i].name);
				_frames.push_back(f);

				v3 scale(frames[i].scale[0],frames[i].scale[1],frames[i].scale[2]);
				v3 translate(frames[i].translate[0],frames[i].translate[1],frames[i].translate[2]);

				for(int j=0; j<header.num_vertices; ++j)
				{
					f._data[j]._pos.x() = frames[i].verts[j].v[0]*frames[i].scale[0]+frames[i].translate[0];
					f._data[j]._pos.y() = frames[i].verts[j].v[1]*frames[i].scale[1]+frames[i].translate[1];
					f._data[j]._pos.z() = frames[i].verts[j].v[2]*frames[i].scale[2]+frames[i].translate[2];
					f._data[j]._nrmidx = frames[i].verts[j].normalIndex;
				}
				delete [] frames[i].verts;
			}
			delete [] frames;
		}

		fin.close();

		return 0;
	}

	void animate(const int _Start, const int _End, int &_Frame, float &_Lerp)
	{
		if((_Frame<_Start) || (_Frame>_End)) _Frame = _Start;

		if( _Lerp>=1.f )
		{
			_Lerp -= 1.f;
			if(++_Frame>=_End) _Frame = _Start;
		}
		draw(_Frame,_Start+(_Frame-_Start+1)%(_End-_Start),_Lerp);
	}

	void draw(const unsigned int _Frame1,const unsigned int _Frame2,const float _Lerp)
	{
		if(_Frame1>=_frames.size() || _Frame2>=_frames.size()) return;

		Vert* v1 = _frames[_Frame1]._data;
		Vert* v2 = _frames[_Frame2]._data;
		float llerp = 1.f-_Lerp;
		for(unsigned int i=0; i<_num_vertices; ++i)
		{
			_vertices[i]._pos = (*v1)._pos*llerp + (*v2)._pos*_Lerp;
			_vertices[i]._nrm = (NormalLUT[(*v1)._nrmidx]*llerp + NormalLUT[(*v2)._nrmidx]*_Lerp).unit();
			*v1++; *v2++;
		}

		glBegin(GL_TRIANGLES);
		for(unsigned int i=0; i<_num_triangles; ++i)
		{
			glTexCoord2fv(_texcoords[_triangles[i]._uv[0]].ptr());
			glNormal3fv(_vertices[_triangles[i]._verts[0]]._nrm.ptr());
			glVertex3fv(_vertices[_triangles[i]._verts[0]]._pos.ptr());
			glTexCoord2fv(_texcoords[_triangles[i]._uv[1]].ptr());
			glNormal3fv(_vertices[_triangles[i]._verts[1]]._nrm.ptr());
			glVertex3fv(_vertices[_triangles[i]._verts[1]]._pos.ptr());
			glTexCoord2fv(_texcoords[_triangles[i]._uv[2]].ptr());
			glNormal3fv(_vertices[_triangles[i]._verts[2]]._nrm.ptr());
			glVertex3fv(_vertices[_triangles[i]._verts[2]]._pos.ptr());
		}
		glEnd();
	}
};


/*
 *	anorms.h - header file
 */
v3 NormalLUT[] = {
	v3( -0.525731f,  0.000000f,  0.850651f ),
	v3( -0.442863f,  0.238856f,  0.864188f ),
	v3( -0.295242f,  0.000000f,  0.955423f ),
	v3( -0.309017f,  0.500000f,  0.809017f ),
	v3( -0.162460f,  0.262866f,  0.951056f ),
	v3(  0.000000f,  0.000000f,  1.000000f ),
	v3(  0.000000f,  0.850651f,  0.525731f ),
	v3( -0.147621f,  0.716567f,  0.681718f ),
	v3(  0.147621f,  0.716567f,  0.681718f ),
	v3(  0.000000f,  0.525731f,  0.850651f ),
	v3(  0.309017f,  0.500000f,  0.809017f ),
	v3(  0.525731f,  0.000000f,  0.850651f ),
	v3(  0.295242f,  0.000000f,  0.955423f ),
	v3(  0.442863f,  0.238856f,  0.864188f ),
	v3(  0.162460f,  0.262866f,  0.951056f ),
	v3( -0.681718f,  0.147621f,  0.716567f ),
	v3( -0.809017f,  0.309017f,  0.500000f ),
	v3( -0.587785f,  0.425325f,  0.688191f ),
	v3( -0.850651f,  0.525731f,  0.000000f ),
	v3( -0.864188f,  0.442863f,  0.238856f ),
	v3( -0.716567f,  0.681718f,  0.147621f ),
	v3( -0.688191f,  0.587785f,  0.425325f ),
	v3( -0.500000f,  0.809017f,  0.309017f ),
	v3( -0.238856f,  0.864188f,  0.442863f ),
	v3( -0.425325f,  0.688191f,  0.587785f ),
	v3( -0.716567f,  0.681718f, -0.147621f ),
	v3( -0.500000f,  0.809017f, -0.309017f ),
	v3( -0.525731f,  0.850651f,  0.000000f ),
	v3(  0.000000f,  0.850651f, -0.525731f ),
	v3( -0.238856f,  0.864188f, -0.442863f ),
	v3(  0.000000f,  0.955423f, -0.295242f ),
	v3( -0.262866f,  0.951056f, -0.162460f ),
	v3(  0.000000f,  1.000000f,  0.000000f ),
	v3(  0.000000f,  0.955423f,  0.295242f ),
	v3( -0.262866f,  0.951056f,  0.162460f ),
	v3(  0.238856f,  0.864188f,  0.442863f ),
	v3(  0.262866f,  0.951056f,  0.162460f ),
	v3(  0.500000f,  0.809017f,  0.309017f ),
	v3(  0.238856f,  0.864188f, -0.442863f ),
	v3(  0.262866f,  0.951056f, -0.162460f ),
	v3(  0.500000f,  0.809017f, -0.309017f ),
	v3(  0.850651f,  0.525731f,  0.000000f ),
	v3(  0.716567f,  0.681718f,  0.147621f ),
	v3(  0.716567f,  0.681718f, -0.147621f ),
	v3(  0.525731f,  0.850651f,  0.000000f ),
	v3(  0.425325f,  0.688191f,  0.587785f ),
	v3(  0.864188f,  0.442863f,  0.238856f ),
	v3(  0.688191f,  0.587785f,  0.425325f ),
	v3(  0.809017f,  0.309017f,  0.500000f ),
	v3(  0.681718f,  0.147621f,  0.716567f ),
	v3(  0.587785f,  0.425325f,  0.688191f ),
	v3(  0.955423f,  0.295242f,  0.000000f ),
	v3(  1.000000f,  0.000000f,  0.000000f ),
	v3(  0.951056f,  0.162460f,  0.262866f ),
	v3(  0.850651f, -0.525731f,  0.000000f ),
	v3(  0.955423f, -0.295242f,  0.000000f ),
	v3(  0.864188f, -0.442863f,  0.238856f ),
	v3(  0.951056f, -0.162460f,  0.262866f ),
	v3(  0.809017f, -0.309017f,  0.500000f ),
	v3(  0.681718f, -0.147621f,  0.716567f ),
	v3(  0.850651f,  0.000000f,  0.525731f ),
	v3(  0.864188f,  0.442863f, -0.238856f ),
	v3(  0.809017f,  0.309017f, -0.500000f ),
	v3(  0.951056f,  0.162460f, -0.262866f ),
	v3(  0.525731f,  0.000000f, -0.850651f ),
	v3(  0.681718f,  0.147621f, -0.716567f ),
	v3(  0.681718f, -0.147621f, -0.716567f ),
	v3(  0.850651f,  0.000000f, -0.525731f ),
	v3(  0.809017f, -0.309017f, -0.500000f ),
	v3(  0.864188f, -0.442863f, -0.238856f ),
	v3(  0.951056f, -0.162460f, -0.262866f ),
	v3(  0.147621f,  0.716567f, -0.681718f ),
	v3(  0.309017f,  0.500000f, -0.809017f ),
	v3(  0.425325f,  0.688191f, -0.587785f ),
	v3(  0.442863f,  0.238856f, -0.864188f ),
	v3(  0.587785f,  0.425325f, -0.688191f ),
	v3(  0.688191f,  0.587785f, -0.425325f ),
	v3( -0.147621f,  0.716567f, -0.681718f ),
	v3( -0.309017f,  0.500000f, -0.809017f ),
	v3(  0.000000f,  0.525731f, -0.850651f ),
	v3( -0.525731f,  0.000000f, -0.850651f ),
	v3( -0.442863f,  0.238856f, -0.864188f ),
	v3( -0.295242f,  0.000000f, -0.955423f ),
	v3( -0.162460f,  0.262866f, -0.951056f ),
	v3(  0.000000f,  0.000000f, -1.000000f ),
	v3(  0.295242f,  0.000000f, -0.955423f ),
	v3(  0.162460f,  0.262866f, -0.951056f ),
	v3( -0.442863f, -0.238856f, -0.864188f ),
	v3( -0.309017f, -0.500000f, -0.809017f ),
	v3( -0.162460f, -0.262866f, -0.951056f ),
	v3(  0.000000f, -0.850651f, -0.525731f ),
	v3( -0.147621f, -0.716567f, -0.681718f ),
	v3(  0.147621f, -0.716567f, -0.681718f ),
	v3(  0.000000f, -0.525731f, -0.850651f ),
	v3(  0.309017f, -0.500000f, -0.809017f ),
	v3(  0.442863f, -0.238856f, -0.864188f ),
	v3(  0.162460f, -0.262866f, -0.951056f ),
	v3(  0.238856f, -0.864188f, -0.442863f ),
	v3(  0.500000f, -0.809017f, -0.309017f ),
	v3(  0.425325f, -0.688191f, -0.587785f ),
	v3(  0.716567f, -0.681718f, -0.147621f ),
	v3(  0.688191f, -0.587785f, -0.425325f ),
	v3(  0.587785f, -0.425325f, -0.688191f ),
	v3(  0.000000f, -0.955423f, -0.295242f ),
	v3(  0.000000f, -1.000000f,  0.000000f ),
	v3(  0.262866f, -0.951056f, -0.162460f ),
	v3(  0.000000f, -0.850651f,  0.525731f ),
	v3(  0.000000f, -0.955423f,  0.295242f ),
	v3(  0.238856f, -0.864188f,  0.442863f ),
	v3(  0.262866f, -0.951056f,  0.162460f ),
	v3(  0.500000f, -0.809017f,  0.309017f ),
	v3(  0.716567f, -0.681718f,  0.147621f ),
	v3(  0.525731f, -0.850651f,  0.000000f ),
	v3( -0.238856f, -0.864188f, -0.442863f ),
	v3( -0.500000f, -0.809017f, -0.309017f ),
	v3( -0.262866f, -0.951056f, -0.162460f ),
	v3( -0.850651f, -0.525731f,  0.000000f ),
	v3( -0.716567f, -0.681718f, -0.147621f ),
	v3( -0.716567f, -0.681718f,  0.147621f ),
	v3( -0.525731f, -0.850651f,  0.000000f ),
	v3( -0.500000f, -0.809017f,  0.309017f ),
	v3( -0.238856f, -0.864188f,  0.442863f ),
	v3( -0.262866f, -0.951056f,  0.162460f ),
	v3( -0.864188f, -0.442863f,  0.238856f ),
	v3( -0.809017f, -0.309017f,  0.500000f ),
	v3( -0.688191f, -0.587785f,  0.425325f ),
	v3( -0.681718f, -0.147621f,  0.716567f ),
	v3( -0.442863f, -0.238856f,  0.864188f ),
	v3( -0.587785f, -0.425325f,  0.688191f ),
	v3( -0.309017f, -0.500000f,  0.809017f ),
	v3( -0.147621f, -0.716567f,  0.681718f ),
	v3( -0.425325f, -0.688191f,  0.587785f ),
	v3( -0.162460f, -0.262866f,  0.951056f ),
	v3(  0.442863f, -0.238856f,  0.864188f ),
	v3(  0.162460f, -0.262866f,  0.951056f ),
	v3(  0.309017f, -0.500000f,  0.809017f ),
	v3(  0.147621f, -0.716567f,  0.681718f ),
	v3(  0.000000f, -0.525731f,  0.850651f ),
	v3(  0.425325f, -0.688191f,  0.587785f ),
	v3(  0.587785f, -0.425325f,  0.688191f ),
	v3(  0.688191f, -0.587785f,  0.425325f ),
	v3( -0.955423f,  0.295242f,  0.000000f ),
	v3( -0.951056f,  0.162460f,  0.262866f ),
	v3( -1.000000f,  0.000000f,  0.000000f ),
	v3( -0.850651f,  0.000000f,  0.525731f ),
	v3( -0.955423f, -0.295242f,  0.000000f ),
	v3( -0.951056f, -0.162460f,  0.262866f ),
	v3( -0.864188f,  0.442863f, -0.238856f ),
	v3( -0.951056f,  0.162460f, -0.262866f ),
	v3( -0.809017f,  0.309017f, -0.500000f ),
	v3( -0.864188f, -0.442863f, -0.238856f ),
	v3( -0.951056f, -0.162460f, -0.262866f ),
	v3( -0.809017f, -0.309017f, -0.500000f ),
	v3( -0.681718f,  0.147621f, -0.716567f ),
	v3( -0.681718f, -0.147621f, -0.716567f ),
	v3( -0.850651f,  0.000000f, -0.525731f ),
	v3( -0.688191f,  0.587785f, -0.425325f ),
	v3( -0.587785f,  0.425325f, -0.688191f ),
	v3( -0.425325f,  0.688191f, -0.587785f ),
	v3( -0.425325f, -0.688191f, -0.587785f ),
	v3( -0.587785f, -0.425325f, -0.688191f ),
	v3( -0.688191f, -0.587785f, -0.425325f )
};


} // namespace dalelib

#endif
